`co(0,7);                               EnCom Functions                                `co();

  `co(12,?);Core Functions          Extended Functions       Message Functions`co();
  `keyword(A) com_port_create,/// com_port_create);      `keyword(L) com_getc_qty,/// com_getc_qty);          `keyword(W) encrypt_msg,/// encrypt_msg);
  `keyword(B) com_port_init,/// com_port_init);        `keyword(M) com_gets,/// com_gets);              `keyword(X) calc_crc,/// calc_crc);
  `keyword(C) com_port_destroy,/// com_port_destroy);     `keyword(N) com_putc_qty,/// com_putc_qty);          `keyword(Y) fmt_msg,/// fmt_msg);
  `keyword(D) com_clear_que,/// com_clear_que);        `keyword(O) send_break,/// send_break);            `keyword(Z) defmt_msg,/// defmt_msg);
  `keyword(E) com_getc,/// com_getc);             `keyword(P) set_xon_flow,/// set_xon_flow);          `keyword(1) get_msg,/// get_msg);
  `keyword(F) com_putc,/// com_putc);             `keyword(Q) set_dcd_flow,/// set_dcd_flow);          `keyword(2) mk_crc_tbl,/// mk_crc_tbl);
  `keyword(G) com_puts,/// com_puts);             `keyword(R) set_dsr_flow,/// set_dsr_flow);
  `keyword(H) prime_tx,/// prime_tx);             `keyword(S) suspend,/// suspend);               `co(12,?);Keyboard/Timer/16550`co();
  `keyword(I) force_tx,/// force_tx);             `keyword(T) wait_for_rx,/// wait_for_rx);           `keyword(3) init_ctrlc_hdlr,/// init_ctrlc_hdlr);
  `keyword(J) com_irq_ctrl,/// com_irq_ctrl);         `keyword(U) wait_for_no_rx,/// wait_for_no_rx);        `keyword(4) end_ctrlc_hdlr,/// end_ctrlc_hdlr);
  `keyword(K) com_232_ctrl,/// com_232_ctrl);         `keyword(V) modem_cmd,/// modem_cmd);             `keyword(5) init_clock,/// init_clock);
                                                   `keyword(6) end_clock,/// end_clock);
                                                   `keyword(7) tone,/// tone);
                                                   `keyword(8) sound_off,/// sound_off);
                                                   `keyword(9) set_idle_func,/// set_idle_func);
                                                   `keyword(0) com_fifo_trigger,/// com_fifo_trigger);
                                                   `keyword(-) com_fifo_ctrl,/// com_fifo_ctrl);

`co(10,1);/// com_port_create`co();      `keyword(source,[EN_MAIN.C]~com_port_create);
    creates the port, initializing it to the values passed as parameters.
  If the function returns a 0, then the port was created, UART initialized,
  and transmit and receive queues allocated.  A negative number is returned
  on error.

  Error Code    Meaning
  -1            Cannot access device.
  -2            Could not allocate receive queue.
  -3            Could not allocate transmit queue.
  -4            Invalid format parameters (baud, parity, data or stop bits).
  -5            Invalid channel (COM1-4).

  See also `keyword(com_port_destroy,/// com_port_destroy);

Prototype:
  int com_port_create( int chan, long baud, int parity, int data_bits,
                       int stop_bits, int rx_size, int tx_size, PORT *cp );

Parameters:
`co(11,1);  int chan`co();
    The channel number #define.  The valid channel defines are COM1, COM2,
    COM3, and COM4.
`co(11,1);  long baud`co();
    The initial baud rate.  A baud rate of 19.2 would be 19200L.
`co(11,1);  int parity`co();
    The initial parity.  Use the character 'N' for none, 'E' for even, 'O'
    for odd, 'M' for mark, and 'S' for space.
`co(11,1);  int data_bits`co();
    The number of data bits per character, and can be from 5 to 8.
`co(11,1);  int stop_bits`co();
    The number of stop bits per character, either 1 or 2.
`co(11,1);  int rx_size`co();
    The size of the receive queue, up to 64K.
`co(11,1);  int tx_size`co();
    The size of the transmit queue, up to 64K.
`co(11,1);  PORT *cp`co();
    A pointer to a PORT structure.  The PORT structure must be either a
    global variable, or a previously allocated structure.  Do NOT pass
    an uninitialized pointer to the function as this routine clears
    the structure!

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( comm_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
  }

`co(10,1);/// com_port_init`co();      `keyword(source,[EN_MAIN.C]~com_port_init);
    initializes the port structure and UART, but does not allocate the
  receive or transmit queues.  This function is called internally by
  `keyword(com_port_create,/// com_port_create); to do the actual initialization.  You may call this
  function any time after com_port_create to change the port parameters
  without affecting the queues and is easier than the alternative of having
  to call com_port_destroy followed by com_port_create.  Like com_port_create
  a 0 is returned on success and a negative number on failure.

  Error Code    Meaning
  -1            Invalid parity (N, O, E, M, or S).
  -2            Invalid number of data bits (5, 6, 7, or 8).
  -3            Invalid number of stop bits (1, or 2).

    You can use this function to change the data format within your program
	at any time with no ill effect.

Prototype:
  int com_port_init( long baud, int parity, int data_bits, int stop_bits,
                     PORT *cp );

Parameters:
`co(11,1);  long baud`co();
    The new baud rate.
`co(11,1);  int parity`co();
    The new parity.  Use the character 'N' for none, 'E' for even, 'O'
    for odd, 'M' for mark, and 'S' for space.
`co(11,1);  int data_bits`co();
    The new number of data bits per char (5 to 8).
`co(11,1);  int stop_bits`co();
    The new number of stop bits per char (1 or 2).
`co(11,1);  PORT *cp`co();
    A pointer to a PORT structure.  The PORT structure must be either a
    global variable, or a previously allocated structure.  Do NOT pass
    an uninitialized pointer to the function.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( comm_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_port_init(2400L, 'E', 7, 1, cp);
  }

`co(10,1);/// com_port_destroy`co();      `keyword(source,[EN_MAIN.C]~com_port_destroy);
    destroys the port created with `keyword(com_port_create,/// com_port_create);.  The transmit and
  receive queues are freed, and the port structure set to inactive.  Be
  sure to use this function to destroy any ports you create.

Prototype:
  void com_port_destroy( PORT *cp );

Parameters:
`co(11,1);  PORT *cp`co();
    A pointer to the PORT structure used in com_port_create.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_port_init(2400L, 'E', 7, 1, cp);
    com_port_destroy(cp);
  }

`co(10,1);/// com_clear_que`co();      `keyword(source,[EN_MAIN.C]~com_clear_que);
    can be use to clear either the port receive queue, the port transmit
  queue, or both.

Prototype:
  void com_clear_que( int mode, PORT *cp );

Parameters:
`co(11,1);  int mode`co();
    The queue or queues to clear.  Use the defines RX and TX to indicate
  the queue.  To specify both queues, use RX | TX.
`co(11,1);  PORT *cp`co();
    A pointer to the PORT structure used in com_port_create.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_clear_que( RX | TX, cp);
  }

`co(10,1);/// com_getc`co();      `keyword(source,[EN_MAIN.C]~com_getc);
    is used to get a character from the port receive queue.  If there is
  no data in the receive queue, that is the rx_cnt member of the port
  structure is 0, then -1 is returned.  If there is data in the receive
  queue, then the next byte is returned. Please note that you can
  check for a queue empty condition, by directly accessing the rx_cnt
  variable of the port structure or using the com_rx_cnt macro.  If 
  "queue status" mode is enabled, com_getc will return an integer, the
  low byte is the character and the high byte is the status. Note that
  it is possible for com_getc to return a -1 as a valid char/status
  combination in this mode.  To prevent this, simply be sure to check
  for data (using com_rx_cnt) before calling com_getc.

Prototype:
  int com_getc( PORT *cp );

Parameters:
`co(11,1);  PORT *cp`co();
    The PORT pointer from which to receive.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  int  next_byte;
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    if (com_rx_cnt(cp) > 0)
      next_byte = com_getc(cp);
  }

`co(10,1);/// com_putc`co();      `keyword(source,[EN_MAIN.C]~com_putc);
    is used to put a character into the port transmit queue.  If there is
  no room left in the queue, then the port member overflow is incremented
  and a -1 is returned.  If the byte is queued, then 0 is returned.
  Since EnCom is both receive and transmit interrupt driven, no other
  function is necessary for the byte to be transmitted!

Prototype:
  int com_putc( int c, PORT *cp );

Parameters:
`co(11,1);  int c`co();
    The byte to put into the transmit queue.
`co(11,1);  PORT *cp`co();
    The PORT pointer for transmission.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_putc('A', cp);
  }

`co(10,1);/// com_puts`co();      `keyword(source,[EN_MAIN.C]~com_puts);
    is used to put a string into the port transmit queue.

Prototype:
  int com_puts( uchar *s, PORT *cp );

Parameters:
`co(11,1);  uchar *s`co();
    The string to transmit.
`co(11,1);  PORT *cp`co();
    The PORT pointer for transmission.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_puts("ATDT1-816-353-0991\r", cp);
  }

`co(10,1);/// prime_tx`co();      `keyword(source,[EN_MAIN.C]~prime_tx);
    "kick starts" the transmit interrupt by disabling then re-enabling the
  THRE (transmit holding register empty) IRQ bit.  This causes the UART to
  generate a THRE interrupt, and start transmission of the bytes that are in
  the queue.  This function is used internally by the library (in `keyword(com_putc,/// com_putc);),
  and is documented for those of you who wish to understand the internal
  workings of the library.

Prototype:
  void prime_tx( PORT *cp );

Parameters:
`co(11,1);  PORT *cp`co();
    The PORT pointer to "kick start".

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    if( !cp->tx_on )
      cp->tx_on = ON, prime_tx(cp);
  }

`co(10,1);/// force_tx`co();      `keyword(source,[EN_MAIN.C]~force_tx);
    forces the character passed to be transmitted without going through
  the usual queueing and interrupt-driven transmission.  It does check
  to see if the UART is ready for the character.  If the character is
  transmitted, a 1 is returned.  If the UART is not ready, the function
  tries for 1 second, and returns 0 if the byte could not be sent.
  This routine will only work if `keyword(init_clock,/// init_clock); has been called.

Prototype:
  force_tx( int c, PORT *cp );

Parameters:
`co(11,1);  int c`co();
    The byte to transmit.
`co(11,1);  PORT *cp`co();
    The PORT pointer for transmission.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    force_tx( 'A', cp);
  }

`co(10,1);/// com_irq_ctrl`co();      `keyword(source,[EN_MAIN.C]~com_irq_ctrl);
    enables or disables the comm interrupts.  This function is used in
  the library internally by `keyword(com_vect_init,/// com_vect_init);, and will not be called by the
  programmer under most circumstances. The current state of the Interrupt
  Enable register is returned.  You can "or" several value together to
  enable/disable several interrupts at once;  the other bits will not be 
  effected.

Prototype:
  int com_irq_ctrl( int state, int mask, PORT *cp );

Parameters:
`co(11,1);  int state`co();
    The state to use, 0 for off and 1 for on.  You may use the defines ON
    and OFF.
`co(11,1);  int mask`co();
    The IRQ bits to set.  The bits are defined as DR_IRQ, THRE_IRQ,
    RLS_IRQ and MS_IRQ.
`co(11,1);  PORT *cp`co();
    The PORT pointer for IRQ control.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
    com_irq_ctrl(ON, DR_IRQ | THRE_IRQ, cp);

`co(10,1);/// com_232_ctrl`co();      `keyword(source,[EN_MAIN.C]~com_232_ctrl);
    turns on or off the control lines DTR, RTS, OUT1, OUT2 and LOOP.  The
  combined state is returned.  To set several at once, simply "or" the
  defines together.  This function is called internally by the library in
  `keyword(com_port_init,/// com_port_init); to set DTR , RTS, & OUT2 on by default.
	NOTE: OUT2 is used to mask interrupts from the serial board on many PC's.
	If this is not set, or is cleared, interrupts generated by the UART will
	not "get through" to the PC.  This is something to watch for when doing
	a LOOP diagnostic check.  Since the UART "disconnects" all of these lines
	from the outside world, the PC will stop getting interrupts, even though
	the UART is trying to generate them.  Therefore, the loopback mode may not
	work on some PC's, at least as far as interrupts go.

Prototype:
  void com_232_ctrl( int state, int mask, PORT *cp );

Parameters:
`co(11,1);  int state`co();
    The state to set, 0 for off and 1 for on.  You may use the defines ON
    and OFF.
`co(11,1);  int mask`co();
    The RS232 control bits to set.  The bits are defined as DTR, RTS,
    OUT1, OUT2, and LOOP.
`co(11,1);  PORT *cp`co();
    The PORT pointer for RS232 control.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
    com_232_ctrl(DTR | RTS, ON, cp);

`co(10,1);/// com_getc_qty`co();      `keyword(source,[EN_SUPP.C]~com_getc_qty);
    is used to get one or more bytes from the port receive queue.  If there
  is less than the requested number of bytes in the receive queue, then
  a -1 is returned, otherwise a 1 is returned.  If "queue status" mode is
  enabled, com_getc_qty will put char/status pairs into "data".  Note that
  in this mode, "qty" will refer to char/status pairs, not bytes in the
  queue, and the size of "data" must be large enough to hold both.

Prototype:
  int com_getc_qty( uchar *data, uint qty, PORT *cp );

Parameters:
`co(11,1);  uchar *data`co();
    A pointer for storage of "qty" bytes/pairs of data from the receive
    queue.
`co(11,1);  uint qty`co();
    The number of bytes/pairs to get from the queue.
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  PORT *cp = calloc(1, sizeof(PORT));
  uchar buffer[80];
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_getc_qty(buffer, 80, cp);
  }

`co(10,1);/// com_gets`co();      `keyword(source,[EN_SUPP.C]~com_gets);
    is used to get a string from the port receive queue, stopping on the
  passed byte, or until the passed quantity of bytes has been retrieved,
  or the queue is empty.  The number of bytes retrieved is returned.
  If the stop byte passed to the function is not 0, then the data will
  not be null-terminated. As with com_getc_qty, if "queue status" mode
  is enabled, "qty" char/status pairs will be returned.

Prototype:
  int com_gets( uchar *data, uchar c, uint qty, PORT *cp );

Parameters:
`co(11,1);  uchar *data`co();
    A pointer for storage of the bytes of data from the receive queue.
`co(11,1);  uchar c`co();
    The stop byte.  Normally this would be 0, but it can be any value.
`co(11,1);  uint qty`co();
    The maximum number of bytes to get from the queue.  If the stop byte
    is matched or the queue drains completely then the returned number
    of bytes will be less than this quantity.
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  uchar buffer[80];
  int   total_rx;
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    total_rx = com_gets(buffer, 0, 80, cp);
  }

`co(10,1);/// com_putc_qty`co();      `keyword(source,[EN_SUPP.C]~com_putc_qty);
    is used to queue up qty bytes in the ports transmit queue, for
  subsequent transmission via the interrupt routine.  If there is not enough
  room in the transmit queue, a -1 is returned.  If the data was queued
  successfully, a 0 is returned.

Prototype:
  int com_putc_qty( uchar *data, uint qty, PORT *cp );

Parameters:
`co(11,1);  uchar *data`co();
    A pointer to the data to queue.
`co(11,1);  uint qty`co();
    The number of bytes of data to queue.
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    com_putc_qty("THIS IS A TEST", 14, cp);
  }

`co(10,1);/// send_break`co();      `keyword(source,[EN_SUPP.C]~send_break);
    is used to set a break condition in the UART for a specified amount
  of time.
  This routine will only work if `keyword(init_clock,/// init_clock); has been called.

Prototype:
  void send_break( int t, PORT *cp );

Parameters:
`co(11,1);  int t`co();
    The number of timer ticks the condition is to be held.
`co(11,1);  PORT *cp`co();
    The PORT pointer for the break.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    send_break(Tics_per_sec * 2, cp);              /* break for 2 seconds */
  }

`co(10,1);/// set_xon_flow`co();      `keyword(source,[EN_SUPP.C]~set_xon_flow);
    sets or resets the XON/XOFF flow control.  The XON/XOFF chars can also
  be set, along with queue high and low water marks.

Prototype:
  int set_xon_flow( int state, int xon_char, int xoff_char,
                        uint highwater, uint lowwater, PORT *cp );

Parameters:
`co(11,1);  int state`co();
    The XON/XOFF state, either 1 or 0.  You may use the ON an OFF defines
    if desired.
`co(11,1);  int xon_char`co();
    The XON character to use.  The define for the most commonly used
    xon character is XON_CHAR.  To specify XON on any character, use the
    XON_ANY define.
`co(11,1);  int xoff_char`co();
    The XOFF character to use.  The define for the most commonly used
    xoff character is XOFF_CHAR.
`co(11,1);  uint highwater`co();
    The number of bytes (less than the size of the queue) to set for XOFF
    triggering.  When the queue fills to this number, an XOFF character
    will automatically be sent.
`co(11,1);  uint lowwater`co();
    The number of bytes to set for XON triggering.  When the queue drains to
    this number, an XON character will automatically be sent.
`co(11,1);  PORT *cp`co();
    The PORT pointer for XON/XOFF flow control.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    set_xon_flow(ON, XON_ANY, XOFF_CHAR, 1800, 200, cp);
  }

`co(10,1);/// set_dcd_flow`co();      `keyword(source,[EN_SUPP.C]~set_dcd_flow);
    sets or resets the DCD flow control.

Prototype:
  int set_dcd_flow( int state, PORT *cp );

Parameters:
`co(11,1);  int state`co();
    The DCD flow control state, either 1 or 0.  You may use the ON an OFF
    defines if desired.
`co(11,1);  PORT *cp`co();
    The PORT pointer for DCD flow control.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    set_dcd_flow(ON, cp);
  }

`co(10,1);/// set_dsr_flow`co();      `keyword(source,[EN_SUPP.C]~set_dsr_flow);
    sets or resets the DSR flow control.

Prototype:
  int set_dsr_flow( int state, PORT *cp );

Parameters:
`co(11,1);  int state`co();
    The DSR flow control state, either 1 or 0.  You may use the ON an OFF
    defines if desired.
`co(11,1);  PORT *cp`co();
    The PORT pointer for DSR flow control.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    ...
    set_dsr_flow(ON, cp);
  }

`co(10,1);/// suspend`co();      `keyword(source,[EN_SUPP.C]~suspend);
    is used to wait for a desired number of clock ticks.  If you have
  used `keyword(set_idle_func,/// set_idle_func); to set a background processing function, it will be
  called for you during the suspend time.
  This routine will only work if `keyword(init_clock,/// init_clock); has been called.

Prototype:
  void suspend( int t );

Parameters:
`co(11,1);  int t`co();
    The number of timer ticks to suspend.

Usage:
  suspend(Tics_per_sec / 2);                      /* wait for 1/2 sec     */

`co(10,1);/// wait_for_rx`co();      `keyword(source,[EN_SUPP.C]~wait_for_rx);
    is used to wait for a given amount of time, or until a specified number
  of characters has been received, whichever comes first.  While waiting,
  if you have set a background processing function with `keyword(set_idle_func,/// set_idle_func);, it
  will be called for you.  This function is useful when it is needed to
  wait for a response from the modem, another computer, etc.
  This routine will only work if `keyword(init_clock,/// init_clock); has been called.

Prototype:
  int wait_for_rx( int t, uint cnt, PORT *cp );

Parameters:
`co(11,1);  int t`co();
    The number of timer ticks to wait.
`co(11,1);  uint cnt`co();
    The number of bytes to wait.
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  com_puts("ATZ\r", cp);                          /* reset the modem     */
  wait_for_rx(Tics_per_sec * 2, 3, cp);            /* wait for the OK     */

`co(10,1);/// wait_for_no_rx`co();      `keyword(source,[EN_SUPP.C]~wait_for_rx);
    is used to wait until no data has been received for a given amount of
  time.  While waiting, if you have set a background processing function
  with `keyword(set_idle_func,/// set_idle_func);, it will be called for you.
  This routine will only work if `keyword(init_clock,/// init_clock); has been called.

Prototype:
  int wait_for_no_rx( int t, PORT *cp );

Parameters:
`co(11,1);  int t`co();
    The number of timer ticks to wait after the last byte is received.
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  com_puts("ATZ\r", cp);                          /* reset the modem     */
  wait_for_no_rx(Tics_per_sec * 2, cp);            /* wait for the OK     */

`co(10,1);/// modem_cmd`co();      `keyword(source,[EN_SUPP.C]~modem_cmd);
    is one of the more versatile functions in EnCom.  With this one function
  you can execute any modem command you wish.  Indeed, all of the modem
  macros defined in ENCOM.H simply call this function with the appropriate
  Hayes command string and timeout.  This function uses `keyword(wait_for_rx,/// wait_for_rx);,
  `keyword(wait_for_no_rx,/// wait_for_no_rx); and finally `keyword(com_getc_qty,/// com_getc_qty); to get the response.  A string
  is returned that contains the response.

  `co(15,?);NOTE:  This function interprets the '~' character as a delay for 1/2 of a
  second.  If you wish to change the pause character to something else,
  simply set the global variable Pause_char.`co();

 CAVEAT: This routine waits until a modem reply starts and then waits     
         until no more data has been received from the modem for 1 second.
         This works for most commands; however, when dialing, if echo is  
         enabled, the modem will echo the dial command and then take as   
         much as 45 seconds or more to connect.  By this time, the routine
         returns the echoed command.  There is no real solution to this   
         unless you wait 45 seconds or more, therefore it is highly       
         recommended that the modem be put into a NO ECHO mode using      
         "modem_echo(OFF,&Port)".  This routine will not see the dial     
         string and will therefore return the result as soon as the modem 
         sends it.                                                        
  
Prototype:
  char *modem_cmd( char *cmd_str, int t, PORT *cp );

Parameters:
`co(11,1);  char *cmd_str`co();
    A string containing the modem command.  A \r is not needed, as the
    function will send a return for you.
`co(11,1);  int p`co();
    An optional numeric parameter if needed.  Use -1 if not.
`co(11,1);  int t`co();
    The number of ticks to wait for a reply.  If there is no response from
    the modem within this time, the string returned will be NULL;
`co(11,1);  PORT *cp`co();
    The PORT pointer for the modem command.

Usage:
  modem_cmd("+++~~~ATH0", -1, Tics_per_sec * 5, cp);  /* hangup the phone */

`co(10,1);/// encrypt_msg`co();      `keyword(source,[EN_MSG.C]~encrypt);
    is used internally by `keyword(fmt_msg,/// fmt_msg); to encrypt the message.  The technique
  used is a simple "exclusive or" of the data with the global variable
  Encrypt_char.  When `keyword(defmt_msg,/// defmt_msg); is called, the message is decrypted by
  the same operation.  For no encryption, the encryption character is set
  to 0.  To setup message encryption for your port, first call the macro
  `keyword(set_encrypt,/// set_encrypt); to set the character, followed by the macro `keyword(init_msg,/// init_msg);.

Prototype:
  void encrypt_msg( uchar *msg, uint len );

Parameters:
`co(11,1);  uchar *msg`co();
    A pointer to the data to encrypt.
`co(11,1);  uint len`co();
    The length of the data.

Usage:
  PORT  *cp = calloc(1, sizeof(PORT));
  uchar msg[] = "THIS IS A READABLE STRING";
  ...
  if( com_port_create(COM2, 2400L, 'N', 8, 1, 2048, 2048, cp) == 0 )
  {
    set_encrypt(0xAA, cp);
    init_msg(cp);
    encrypt_msg(msg, strlen(msg));
    ...
  }

`co(10,1);/// calc_crc`co();      `keyword(source,[EN_MSG.C]~calc_crc);
    is used to calculate the CRC value for a length of bytes.  This
  function is used internally by `keyword(fmt_msg,/// fmt_msg);, `keyword(defmt_msg,,/// defmt_msg,); and `keyword(get_msg,/// get_msg); to ensure
  data integrity.  You may use this routine if you wish to find the CRC
  value for a given number of bytes.  Please note that this routine uses
  the global CRC table, and assumes that the table has been initialized
  by calling `keyword(mk_crc_tbl,/// mk_crc_tbl);.

Prototype:
  uint calc_crc( uchar *msg, uint len );

Parameters:
`co(11,1);  uchar *msg`co();
    A pointer to the data for CRC calculation.
`co(11,1);  uint len`co();
    The length of the data.

Usage:
  uchar msg[] = "THIS IS A TEST";
  uint  crc_val;
  ...
  mk_crc_tbl( 0xFFFF, CRCCITT_REV, gen_crc_rev );
  crc_val = calc_crc(  msg, strlen(msg) );

`co(10,1);/// fmt_msg`co();      `keyword(source,[EN_MSG.C]~fmt_msg);
    formats a message by "quoting" the data and appending a 16 bit CRC to
  the end, followed by an `keyword(End_flag,[ENCOM.HLP]uchar End_flag);. This is useful for "packetizing"
  data for transmission to a remote computer.  By using the messaging
  routines, you can guarantee that the data will be received at the
  remote computer on a message by message basis, with any errors in
  transmission being automatically detected.
  
  When formatting a message, be sure the destination area is longer than the
  source message as the quoting process can cause an expansion to occur.
  Typically, a few percent is enough but to be perfectly safe, make it (2 *
  source size) + 5.  The extra five bytes are for the CRC and End_flag.
  This assumes that every byte has to be quoted except for the End_flag
  which is stored "in the clear".  The function will return the number of
  bytes that are stored in the destination area.

Prototype:
  uint fmt_msg( uchar *s, uchar *d, uint len );

Parameters:
`co(11,1);  uchar *s`co();
    The source data pointer.
`co(11,1);  uchar *d`co();
    The destination data pointer.
`co(11,1);  uint len`co();
    The length of the source data.

Usage:
  uchar src[] = "THIS IS A TEST";
  uchar dest[80];
  uint  dest_len;
  ...
  mk_crc_tbl( 0xFFFF, CRCCITT_REV, gen_crc_rev );
  dest_len = fmt_msg( src, dest, strlen(src) );

`co(10,1);/// defmt_msg`co();      `keyword(source,[EN_MSG.C]~defmt_msg);
    is the complement to `keyword(fmt_msg,/// fmt_msg);. This function takes the formatted
  message and returns it back to its original state.  If there was an
  error in transmission (the CRC does not match the calculated value),
  then a 0 is returned, otherwise the length of the destination is returned.
  defmt_msg is the equivalent to get_msg, except that it extracts data from
  a memory buffer, not the receive queue.

Prototype:
  uint defmt_msg( uchar *src, uchar *dest );

Parameters:
`co(11,1);  uchar *src`co();
    The source data pointer.
`co(11,1);  uchar *dest`co();
    The destination data pointer.

Usage:
  uchar src[] = "THIS IS A TEST";
  uchar fmt[80], final[80];
  uint  dest_len, defmt_len;
  ...
  mk_crc_tbl( 0xFFFF, CRCCITT_REV, gen_crc_rev );
  dest_len = fmt_msg( src, fmt, strlen(src) );
  defmt_len = defmt_msg( fmt, final );
  if ( defmt_len != strlen(src) )
    printf("error!");

`co(10,1);/// get_msg`co();      `keyword(source,[EN_MSG.C]~get_msg);
    gets one message from the receive queue of the port.  Use the `keyword(msg_cnt,#define msg_cnt);
  macro to determine if there is a message currently in the port receive
  queue. Like defmt_msg, the length of the message is returned if the CRC
  matches, otherwise a 0 is returned, and the message should not be used
  as it is corrupted.  get_msg is the equivalent to fmt_msg, except that it
  extracts data from the receive queue, not a memory buffer.  (The interrupt
  routine increments the port message counter when a message is received,
  and get_msg decrements it.)

Prototype:
  uint get_msg( uchar *dest, PORT *cp );

Parameters:
`co(11,1);  uchar *dest`co();
    The destination data pointer.
`co(11,1);  PORT *cp`co();
    The PORT pointer that has the message in its receive queue.

Usage:
  uchar buffer[1024];
  ...
   if ( msg_cnt(cp) )
     dest_len = get_msg(buffer, cp);

`co(10,1);/// mk_crc_tbl`co();      `keyword(source,[EN_MSG.C]~mk_crc_tbl);
    creates the global Crc_tbl used in `keyword(calc_crc,/// calc_crc);, which is called by the
  message formatting functions.  Any CRC preset (the most common are
  preset all zeros or all ones), any polynomial, and both forward and
  reverse directions can be specified.

Prototype:
  void mk_crc_tbl( ushort init, ushort poly, ushort (*crcfn)() );

Parameters:
`co(11,1);  ushort init`co();
    The CRC preset value, typically 0x0000 or 0xFFFF.
`co(11,1);  ushort poly`co();
    The CRC polynomial value.  Both CRCCITT and CRC16 are supported.  You
    may use one of the pre-defined values CRCCITT, CRCCITT_REV, CRC16, or
    CRC16_REV for this parameter.
`co(11,1);  ushort (*crcfn)()`co();
    A pointer to the function that generates the table.  We supply two
    functions you may call, namely gen_crc and gen_crc_rev.

Usage:
  mk_crc_tbl( 0xFFFF, CRCCITT, gen_crc );

`co(10,1);/// init_ctrlc_hdlr`co();      `keyword(source,[EN_KBD.C]~init_ctrlc_hdlr);
    sets up a keyboard interrupt handler that translates the keys Ctrl-C and
	Ctrl-Break to the benign value 0x2E1E. This prevents the infamous "^C"
	from being displayed on your screen.  Call this function at the beginning
	of your program, and the corresponding `keyword(end_ctrlc_hdlr,/// end_ctrlc_hdlr); at the end of your
	program.

  NOTE: When either of these keys are pressed, the bios will give you back
        the value 0x1E.  If you wish, you can translate this yourself back
        to 0x03, which is Ctrl-C.

Prototype:
  void init_ctrlc_hdlr();

Parameters:
    None.

Usage:
  main()
  {
  	init_ctrlc_hdlr();
  	...
  	end_ctrlc_hdlr();
  }

`co(10,1);/// end_ctrlc_hdlr`co();      `keyword(source,[EN_KBD.C]~end_ctrlc_hdlr);
    terminates the keyboard interrupt handler, and restores the old key
	interrupt vector.  It is the complement to init_ctrlc_hdlr.  If you called
	init_ctrlc_hdlr, you MUST call end_ctrlc_hdlr before you exit the program.

Prototype:
  void end_ctrlc_hdlr();

Parameters:
  None.

Usage:
  main()
  {
  	init_ctrlc_hdlr();
  	...
  	end_ctrlc_hdlr();
  }

`co(10,1);/// init_clock`co();      `keyword(source,[EN_KBD.C]~init_clock);
  	takes over the PC timer interrupt and sets the timer rate to the desired
	value.  This function is necessary as several of the timing functions
	in the library utilize timers incremented in our custom timer interrupt
	function.  Be sure to call `keyword(end_clock,/// end_clock); before you exit your program.

Prototype:
  void init_clock( uint speed );

Parameters:
`co(11,1);	uint  speed`co();
    The countdown value for the PC timer chip.  0xffff is the standard
    18.2 times per second. 0x7fff would increase the rate by 2, 0x3333
    gives exactly 91 ticks per second, and is recommended.

Usage:
  main()
  {
    init_clock(0x3333);											/* speed up by factor of 5 */
  	...
    end_clock();
  }

`co(10,1);/// end_clock`co();   `keyword(source,[EN_KBD.C]~end_clock);
  	restores the timer interrupt and rate.  If you used `keyword(init_clock,/// init_clock);, be
  sure to call this function before exiting your program.

Prototype:
	void end_clock();

Parameters:
 	None.

Usage:
  main()
  {
    init_clock(0x3333);											/* speed up by factor of 5 */
  	...
    end_clock();
  }
  
`co(10,1);/// tone`co();      `keyword(source,[EN_KBD.C]~tone);
  	Sounds the PC speaker at the desired frequency for the desired amount of
	time measured in timer tics. (91/s).  There is no need to turn the sound
	off, this is automatically handled by the timer interrupt.  It is
	perfectly safe to call tone even if a tone is already in progress.  The
	new frequency and time will override the current values.  If you need to
	turn the speaker off before the time has expired call `keyword(sound_off,/// sound_off);.
  This routine will only work if `keyword(init_clock,/// init_clock); has been called.

Prototype:
	void tone( uint freq, int dur );

Parameters:
`co(11,1);	uint  freq`co();
    The desired frequency in hertz or cycles-per-second.   
`co(11,1);	int    dur`co();
    The duration of the tone in clock tics (91 per second).

Usage:
  if( error )
  	tone(1024, 91);											  /* sound at 1k for 1 second */

`co(10,1);/// sound_off`co();      `keyword(source,[EN_KBD.C]~sound_off);
  	turns off any speaker sound in progress.  It is not necessary to call
	this function unless you want to turn off a tone before its duration time
	has expired.

Prototype:
  void sound_off();

Parameters:
  None.
  
Usage:
  tone(1024, 91);
  sound_off();

`co(10,1);/// set_idle_func`co();      `keyword(source,[EN_KBD.C]~set_idle_func);
  	sets a background processing function that the library will call
  whenever it is involved in time-critical functions like `keyword(wait_for_rx,/// wait_for_rx);,
  `keyword(wait_for_no_rx,/// wait_for_no_rx); and `keyword(suspend,/// suspend);.  A good candidate might be a function that
  prints in the background.

Prototype:
  void set_idle_func( int (*func_ptr)() )

Parameters:
`co(11,1);	int (*func_ptr)()`co();
		A pointer to the function you wish to install as the idle function.
  
Usage:
  int process_routine()
  { ... }
  
  main()
  {
  	set_idle_func( process_routine );
  	...
  }

`co(10,1);/// com_fifo_trigger`co();      `keyword(source,[EN_MAIN.C]~com_fifo_trigger);
    is used to set the trigger level for the 16550 receive FIFO.  This can
  be set to 1, 4, 8, or 14 using the defines RX_TRIG_1, RX_TRIG_4, RX_TRIG_8,
  and RX_TRIG_14.

Prototype:
int com_fifo_trigger( int level, PORT *cp );

Parameters:
`co(11,1);  int level`co();
    The trigger level mask, i.e. RX_TRIG_8 (don't pass the decimal value 8).
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  com_fifo_trigger( RX_TRIG_4, cp);				        /* set trigger level   */

`co(10,1);/// com_fifo_ctrl`co();      `keyword(source,[EN_MAIN.C]~com_fifo_ctrl);
    is used to set the FIFO's on or off, clear the rx or tx FIFO's, etc.
  Macros are provided to do these functions.

Prototype:
int com_fifo_ctrl( int state, int mask, PORT *cp );

Parameters:
`co(11,1);  int state`co();
    The FIFO state, ON or OFF.
`co(11,1);  int mask`co();
    The #defined bit masks i.e. FIFO_ON or RX_FIFO_RESET.
`co(11,1);  PORT *cp`co();
    The PORT pointer to use.

Usage:
  com_fifo_ctrl(ON, RX_FIFO_RESET, cp);		        /* clear rx fifo       */

`co(0,7);                            EnCom General Macros                              `co();

  `co(12,?);General Macros`co();
	#define lower(x, y)	  	     (((x) < (y)) ? (x) : (y))
	#define upper(x, y) 	 	     (((x) > (y)) ? (x) : (y))
	#define lobyte(c)			       (uchar) ((c) & 0x00ff)
	#define hibyte(c)			       (uchar) ((c) >> 8)
	#define range(l,b,h)         ((((b) >= (l)) && ((b) <= (h))) ? 1:0)
	#define swap( a,b,c)	       ((c) = (a), (a) = (b), (b) = (c))
	#define min(x, y) 		 	     (((x) < (y)) ? (x) : (y))
	#define max(x, y)  			     (((x) > (y)) ? (x) : (y))

`co(0,7);                              EnCom Comm Macros                               `co();

  `co(12,?);Comm Macros`co();
    These macros are used to access the queue counts and status.

	#define com_tx_cnt(cp)       ((cp)->tx_cnt)
	#define com_rx_cnt(cp)       ((cp)->rx_cnt)
	#define com_tx_free(cp)      ((cp)->tx_size - (cp)->tx_cnt)
	#define com_rx_free(cp)      ((cp)->rx_size - (cp)->rx_cnt)
	#define is_rx_empty(cp)      ((cp)->rx_cnt ? 1 : 0)
	#define is_rx_full(cp)       ((cp)->rx_size == (cp)->rx_cnt)
	#define is_tx_empty(cp)      ((cp)->tx_cnt ? 1 : 0)
	#define is_tx_full(cp)       ((cp)->tx_size == (cp)->tx_cnt)

`co(0,7);                             EnCom Message Macros                             `co();

  `co(12,?);Message Macros`co();
    These macros are used to setup the messaging facility.  Be sure to call
  init_msg after calling set_encrypt and set_match or the messaging
  routines will not operate as desired.

	#define set_encrypt(c, cp)	 ((cp)->encrypt_char = c)
	#define set_match(c, cp)		 ((cp)->match_char = c)
	#define init_msg(cp)         (End_flag = (cp)->match_char,
	                              Encrypt_char = (cp)->encrypt_char)
	#define msg_cnt(cp)					 ((cp)->msg_cnt)
	#define clear_msg_cnt(cp)    ((cp)->msg_cnt = 0)

    These macros are used to enable/disable, and check on the `keyword(queue status,[ENCOM.HLP]EnCom's "Queue Status");
  mode.

	#define is_queue_status(x,cp)  ((cp)->que_stat_flag)
	#define set_queue_status(x,cp) ((cp)->que_stat_flag = x)

`co(0,7);                          EnCom Handshaking Macros                            `co();

  `co(12,?);Handshaking Macros`co();
    These macros/functions allow you to set RTS/CTS, DCD, or DSR flow
	control using the set_ macros.  You can determine if flow control is
	enabled by using the is_ macros, and you can look at the current state
	using the _state macros.

  XON/XOFF flow control is set by the function `keyword(set_xon_flow,/// set_xon_flow);.
  DCD flow flow control is set by the function `keyword(set_dcd_flow,/// set_dcd_flow);.
  DSR flow flow control is set by the function `keyword(set_dsr_flow,/// set_dsr_flow);.

  #define set_cts_flow(x,cp)   ((cp)->rts_cts_mode = x)

	#define is_cts_flow(cp)	     ((cp)->rts_cts_mode)
	#define is_dcd_flow(cp) 	   ((cp)->dcd_mode)
	#define is_dsr_flow(cp)      ((cp)->dsr_mode)
	#define is_xon_flow(cp)		   ((cp)->xon_xoff_mode)

	#define cts_flow_state(cp)	 ((cp)->rts_cts_state)
	#define dcd_flow_state(cp) 	 ((cp)->dcd_state)
	#define dsr_flow_state(cp)   ((cp)->dsr_state)
	#define xon_flow_state(cp)	 ((cp)->xon_xoff_state)

	  The following macros set and get the low and high water marks for
	XON/XOFF and high speed RTS/CTS flow control.  These values are
	initialized to 10% and 90% of the receive queue size when the comm port
	is created.

	#define set_lowwater(l,cp)   ((cp)->lowwater = l)
	#define set_highwater(h,cp)  ((cp)->highwater = h)
	#define get_lowwater(cp)     ((cp)->lowwater)
	#define get_highwater(cp)    ((cp)->highwater)

`co(0,7);                             EnCom Modem Macros                               `co();

  `co(12,?);Modem Command Macros`co();
	  These macros provide an "easier to read" method of performing common
	modem commands.  Notice how they are all implemented with the powerful
	`keyword(modem_cmd,/// modem_cmd); function.
	
	#define modem_reset(cp)  	       modem_cmd("ATZ",-1,Tics_per_sec*2,(cp))
	#define modem_dial(s,cp)         modem_cmd(s,-1,Tics_per_sec*45,(cp))
	#define modem_answer(cp)         modem_cmd("ATA",-1,Tics_per_sec,(cp))
	#define modem_hangup(cp)         modem_cmd("+++~~~ATH0",
	                                           -1,Tics_per_sec*5,(cp))
	#define modem_online(cp)		   	 modem_cmd("ATO",-1,Tics_per_sec,(cp))
	#define modem_repeat(cp)  	     modem_cmd("A/",-1,Tics_per_sec,(cp))
	#define modem_speaker(x,cp) 	   modem_cmd("ATM",x,Tics_per_sec,(cp))
	#define modem_echo(x,cp)  		   modem_cmd("ATE",x,Tics_per_sec,(cp))
	#define modem_word_reply(x,cp)   modem_cmd("ATV",x,Tics_per_sec,(cp))
	#define modem_extend_reply(x,cp) modem_cmd("ATX",x,Tics_per_sec,(cp))
	#define modem_duplex(x,cp)		   modem_cmd("ATF",x,Tics_per_sec,(cp))
	#define modem_quiet(x,cp)		   	 modem_cmd("ATQ",x,Tics_per_sec,(cp))

  `co(12,?);Current Modem Control Macros`co();
    These macros allow you to check the current state of the modem control
  lines.
  
	#define is_dtr(cp)            ((inbyte((cp)->uart[MCR]) & DTR)  ? 1 : 0)
	#define is_rts(cp)            ((inbyte((cp)->uart[MCR]) & RTS)  ? 1 : 0)
	#define is_out1(cp)           ((inbyte((cp)->uart[MCR]) & OUT1) ? 1 : 0)
	#define is_out2(cp)           ((inbyte((cp)->uart[MCR]) & OUT2) ? 1 : 0)
	#define is_loop(cp)           ((inbyte((cp)->uart[MCR]) & LOOP) ? 1 : 0)

  `co(12,?);Current Modem Status Macros`co();
    These macros allow you to check the current state of the modem status
  lines.  Use the static status macros for "block checking", calling
  clear_mstatus for each block.
	
	#define is_dcd(cp)            ((inbyte((cp)->uart[MSR]) & DCD) ? 1 : 0)
	#define is_cts(cp)            ((inbyte((cp)->uart[MSR]) & CTS) ? 1 : 0)
	#define is_dsr(cp)            ((inbyte((cp)->uart[MSR]) & DSR) ? 1 : 0)
	#define is_ri(cp)	            ((inbyte((cp)->uart[MSR]) & RI ) ? 1 : 0)

  `co(12,?);Static Modem Status Macros`co();
	#define clear_mstatus(cp)		   ((cp)->modem_status = 0)
	#define is_sdcd(cp)           (((cp)->smodem_status & DCD)  ? 1 : 0)
	#define is_scts(cp)           (((cp)->smodem_status & CTS)  ? 1 : 0)
	#define is_sdsr(cp)           (((cp)->smodem_status & DSR)  ? 1 : 0)
	#define is_sri(cp)            (((cp)->smodem_status & RI )  ? 1 : 0)

	#define is_sddcd(cp)          (((cp)->smodem_status & DDCD) ? 1 : 0)
	#define is_sdcts(cp)          (((cp)->smodem_status & DCTS) ? 1 : 0)
	#define is_sddsr(cp)          (((cp)->smodem_status & DDSR) ? 1 : 0)
	#define is_sdri(cp)           (((cp)->smodem_status & DRI)  ? 1 : 0)

`co(0,7);                          EnCom Line Status Macros                            `co();

    These macros allow you to check the current state of the line status.
  Use the static status macros for "block checking", calling clear_lstatus
  for each block.

  `co(12,?);Current Line Status Macros`co();
	#define is_overrun_err(cp)    ((inbyte((cp)->uart[LSR]) & OE)  ? 1 : 0)
	#define is_parity_err(cp)     ((inbyte((cp)->uart[LSR]) & PE)  ? 1 : 0)
	#define is_framing_err(cp)    ((inbyte((cp)->uart[LSR]) & FE)  ? 1 : 0)
	#define is_break(cp)			    ((inbyte((cp)->uart[LSR]) & BI)  ? 1 : 0)

  `co(12,?);Static Line Status Macros`co();
	#define clear_lstatus(cp)		   ((cp)->sline_status = 0)
	#define is_soverrun_err(cp)   (((cp)->sline_status & OE)  ? 1 : 0)
	#define is_sparity_err(cp)    (((cp)->sline_status & PE)  ? 1 : 0)
	#define is_sframing_err(cp)   (((cp)->sline_status & FE)  ? 1 : 0)
	#define is_sbreak(cp)			    (((cp)->sline_status & BI)  ? 1 : 0)

`co(0,7);                          EnCom 16550 Support Macros                          `co();

		These macros support the 16550AF and 16550AFN FIFO parts.  They allow
	you do enable/disable the FIFO's and set and get the receive FIFO trigger
	level.  The trigger level is set by the function `keyword(com_fifo_trigger,/// com_fifo_trigger);.

  `co(12,?);16550 FIFO and Trigger Macros`co();
	#define fifo_enable(cp)   		(com_fifo_ctrl( ON,FIFO_ON,(cp)),
																	(cp)->fifos_enabled =  ON)
	#define fifo_disable(cp)  		(com_fifo_ctrl(OFF,FIFO_ON,(cp)),
																	(cp)->fifos_enabled = OFF)
	#define fifo_rx_reset(cp) 		(com_fifo_ctrl(ON,RX_FIFO_RESET,(cp)))
	#define fifo_tx_reset(cp) 		(com_fifo_ctrl(ON,TX_FIFO_RESET,(cp)))

	#define is_fifo_enabled(cp)   ((cp)->fifos_enabled)
	#define get_trigger_level(cp) ((cp)->rx_trig_level)
